/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the 'License');
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import * as path from 'path';
import { expect, suite, test } from 'vitest';

import ts from '~/vendor/ohos-typescript';

// import adapter before any dsl.
import { mockAdapter, mockEngine } from '../mock';

import ExampleDsl from '../mock/rules/Example';
import NativeExample from '../mock/rules/NativeExample';

suite('DSL test', () => {
    const GOOD_KIND = ts.SyntaxKind.BinaryExpression;
    const BAD_KIND = ts.SyntaxKind.AwaitExpression;

    function getGoodNode(pos: number): ts.Node {
        return {
            kind: GOOD_KIND,
            right: { kind: GOOD_KIND },
            pos: pos,
        } as unknown as ts.Node;
    }

    function getBadNode(pos: number): ts.Node {
        switch (pos % 4) {
            case 0:
                return {
                    kind: GOOD_KIND,
                    right: { kind: BAD_KIND },
                    pos: pos,
                } as unknown as ts.Node;
            case 1:
                return {
                    kind: GOOD_KIND,
                    right: { kind: BAD_KIND },
                    pos: pos,
                } as unknown as ts.Node;
            case 2:
                return {
                    kind: BAD_KIND,
                    right: { kind: GOOD_KIND },
                    pos: pos,
                } as unknown as ts.Node;
            case 3:
                return {
                    kind: BAD_KIND,
                    pos: pos,
                } as unknown as ts.Node;
        }

        return {
            kind: BAD_KIND,
            pos: pos,
        } as ts.Node;
    }

    const GOOD_LINES = [1, 3, 9];
    const REPORTED_LINES = [1, 9];

    mockEngine.configure({ ignoreLines: [3] });
    for (let i = 1; i <= 10; i++) {
        if (GOOD_LINES.includes(i)) {
            mockEngine.addNode(getGoodNode(i));
        } else {
            mockEngine.addNode(getBadNode(i));
        }
    }

    test('built-in DSL', () => {
        mockEngine.clear();
        mockEngine.submit([mockAdapter.loadDslRule(ExampleDsl)]);

        const lines = mockEngine.getIssueLines();
        expect(lines.length).toBe(REPORTED_LINES.length);
        for (let i = 0; i < REPORTED_LINES.length; i++) {
            expect(lines[i]).toBe(REPORTED_LINES[i]);
        }
    });

    // Load external DSL from other packages may have dependency issues.
    // Further investigation required.
    test('external DSL', async () => {
        const DSL_PATH = path.resolve(__dirname, '../mock/rules/Example2.ts');

        mockEngine.clear();
        mockEngine.submit([await mockAdapter.loadDslRuleFromFile(DSL_PATH)]);

        const lines = mockEngine.getIssueLines();
        expect(lines.length).toBe(REPORTED_LINES.length);
        for (let i = 0; i < REPORTED_LINES.length; i++) {
            expect(lines[i]).toBe(REPORTED_LINES[i]);
        }
    });

    test('native DSL', () => {
        mockEngine.clear();
        mockEngine.submit([mockAdapter.loadDslRule(NativeExample)]);

        const lines = mockEngine.getIssueLines();
        expect(lines.length).toBe(REPORTED_LINES.length);
        for (let i = 0; i < REPORTED_LINES.length; i++) {
            expect(lines[i]).toBe(REPORTED_LINES[i]);
        }
    });

    test('external native DSL', async () => {
        const DSL_PATH = path.resolve(__dirname, '../mock/rules/NativeExample2.ts');

        mockEngine.clear();
        mockEngine.submit([await mockAdapter.loadDslRuleFromFile(DSL_PATH)]);

        const lines = mockEngine.getIssueLines();
        expect(lines.length).toBe(REPORTED_LINES.length);
        for (let i = 0; i < REPORTED_LINES.length; i++) {
            expect(lines[i]).toBe(REPORTED_LINES[i]);
        }
    });
});
